package com.uinnova.product.eam.service.bm.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.binary.core.util.BinaryUtils;
import com.google.common.collect.Lists;
import com.uinnova.product.eam.base.model.BaseQueryDiagramDto;
import com.uinnova.product.eam.base.util.EamUtil;
import com.uinnova.product.eam.comm.model.es.EamCategory;
import com.uinnova.product.eam.feign.workable.FlowableFeign;
import com.uinnova.product.eam.feign.workable.entity.PorcessResponse;
import com.uinnova.product.eam.model.bm.EamDataUpdateInfo;
import com.uinnova.product.eam.model.bm.EamDiagramUpdateInfo;
import com.uinnova.product.eam.model.diagram.DiagramNodeJson;
import com.uinnova.product.eam.model.dm.bean.DataModelEntityNodeVo;
import com.uinnova.product.eam.service.EamCategorySvc;
import com.uinnova.product.eam.service.bm.IEamFlowModelSvc;
import com.uinnova.project.api.diagram.v2.client.ESDiagramApiClient;
import com.uinnova.project.base.diagram.comm.model.ESDiagram;
import com.uinnova.project.base.diagram.comm.model.ESDiagramDTO;
import com.uinnova.project.base.diagram.comm.model.ESDiagramLink;
import com.uinnova.project.base.diagram.comm.model.ESDiagramNode;
import com.uino.api.client.permission.IUserApiSvc;
import com.uino.bean.cmdb.base.LibType;
import com.uino.bean.permission.base.SysUser;
import com.uino.bean.permission.query.CSysUser;
import com.uino.util.sys.SysUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 流程模型树业务接口实现
 * @author ch
 */
@Slf4j
@Service
public class EamFlowModelSvcImpl implements IEamFlowModelSvc {

    @Resource
    private EamCategorySvc categorySvc;
    @Resource
    private ESDiagramApiClient diagramApiClient;
    @Autowired
    private FlowableFeign flowableFeign;
    @Autowired
    private IUserApiSvc userApiSvc;

    @Override
    public List<EamDiagramUpdateInfo> queryModelUpdateInfo(List<Long> dirIds, String diagramId) {
        String ownerCode = SysUtil.getCurrentUserInfo().getLoginCode();
        List<EamDiagramUpdateInfo> result = new ArrayList<>();
        List<ESDiagram> diagramList;
        if (!BinaryUtils.isEmpty(diagramId)) {
            Long id = diagramApiClient.queryDiagramInfoByEnergy(diagramId);
            ESDiagram diagram = diagramApiClient.querySimpleDiagramInfoById(id);
            diagramList = Lists.newArrayList(diagram);
        } else {
            diagramList = diagramApiClient.selectByDirIds(dirIds, null, ownerCode, Lists.newArrayList(0));
        }
        if (BinaryUtils.isEmpty(diagramList)) {
            return Collections.emptyList();
        }
        List<Long> diagramIdList = diagramList.stream().map(ESDiagram::getId).collect(Collectors.toList());
        List<String> releaseEnergy = diagramList.stream().map(ESDiagram::getReleaseDiagramId).filter(Objects::nonNull).collect(Collectors.toList());
        if (!BinaryUtils.isEmpty(releaseEnergy)) {
            List<ESDiagram> releaseDiagrams = diagramApiClient.queryDBDiagramInfoByIds(releaseEnergy.toArray(new String[]{}));
            List<Long> releaseDiagramIdList = releaseDiagrams.stream().map(ESDiagram::getId).collect(Collectors.toList());
            diagramIdList.addAll(releaseDiagramIdList);
        }
        List<ESDiagramNode> nodeList = diagramApiClient.selectNodeByDiagramIds(diagramIdList);
        Map<String, List<ESDiagramNode>> nodeGroup = nodeList.stream().collect(Collectors.groupingBy(ESDiagramNode::getdEnergy));
        List<ESDiagramLink> linkList = diagramApiClient.selectLinkByDiagramIds(diagramIdList);
        Map<String, List<ESDiagramLink>> linkGroup = linkList.stream().collect(Collectors.groupingBy(ESDiagramLink::getdEnergy));
        if (!BinaryUtils.isEmpty(dirIds)) {
            List<EamCategory> dirList = categorySvc.getByIds(dirIds, LibType.PRIVATE);
            dirList.sort(Comparator.comparing(EamCategory::getDirLvl));
            List<Long> sortDirIds = dirList.stream().map(EamCategory::getId).collect(Collectors.toList());
            List<String> sortDiagramIds = dirList.stream().map(EamCategory::getDiagramId).filter(Objects::nonNull).collect(Collectors.toList());
            diagramList.sort(Comparator.comparing((ESDiagram each) -> sortDirIds.indexOf(each.getDirId())).reversed().thenComparing(each -> sortDiagramIds.contains(each.getDEnergy())).reversed());
        }
        for (ESDiagram diagram : diagramList) {
            EamDiagramUpdateInfo info = new EamDiagramUpdateInfo();
            info.setDiagramName(diagram.getName());
            if (BinaryUtils.isEmpty(diagram.getReleaseDiagramId())) {
                info.setNewDiagram(true);
            }
            getDataUpdate(diagram, nodeGroup, linkGroup, info);
            if (!BinaryUtils.isEmpty(diagram.getReleaseDiagramId()) && !info.getAdd() && !info.getDelete() && !info.getUpdate()) {
                continue;
            }
            result.add(info);
        }
        return result;
    }

    /**
     * 对比资产视图视图节点或线更新状态
     *
     * @param diagram   视图
     * @param nodeGroup 节点分组
     * @param linkGroup 线分组
     */
    private void getDataUpdate(ESDiagram diagram, Map<String, List<ESDiagramNode>> nodeGroup, Map<String, List<ESDiagramLink>> linkGroup, EamDiagramUpdateInfo result) {
        List<EamDataUpdateInfo> dataInfo = new ArrayList<>();
        List<ESDiagramNode> nodeList = nodeGroup.getOrDefault(diagram.getDEnergy(), new ArrayList<>());
        Map<String, ESDiagramNode> nodeMap = nodeList.stream().filter(each -> !BinaryUtils.isEmpty(each.getCiCode())).collect(Collectors.toMap(ESDiagramNode::getCiCode, each -> each, (k1, k2) -> k2));
        List<ESDiagramLink> linkList = linkGroup.getOrDefault(diagram.getDEnergy(), new ArrayList<>());
        Map<String, ESDiagramLink> linkMap = linkList.stream().filter(each -> !BinaryUtils.isEmpty(each.getUniqueCode())).collect(Collectors.toMap(ESDiagramLink::getUniqueCode, each -> each, (k1, k2) -> k2));
        Map<String, ESDiagramNode> releaseNodeMap = new HashMap<>(16);
        Map<String, ESDiagramNode> releaseNodeKeyMap = new HashMap<>(16);
        Map<String, ESDiagramLink> releaseLinkMap = new HashMap<>(16);
        Map<String, ESDiagramLink> releaseLinkKeyMap = new HashMap<>(16);
        List<ESDiagramNode> releaseNodeList = null;
        List<ESDiagramLink> releaseLinkList = null;
        if (!BinaryUtils.isEmpty(diagram.getReleaseDiagramId())) {
            releaseNodeList = nodeGroup.get(diagram.getReleaseDiagramId());
            if (!BinaryUtils.isEmpty(releaseNodeList)) {
                releaseNodeMap = releaseNodeList.stream().filter(each -> !BinaryUtils.isEmpty(each.getCiCode())).collect(Collectors.toMap(ESDiagramNode::getCiCode, each -> each, (k1, k2) -> k2));
                releaseNodeKeyMap = releaseNodeList.stream().collect(Collectors.toMap(ESDiagramNode::getKey, each -> each, (k1, k2) -> k2));
            }
            releaseLinkList = linkGroup.get(diagram.getReleaseDiagramId());
            if (!BinaryUtils.isEmpty(releaseLinkList)) {
                releaseLinkMap = releaseLinkList.stream().filter(each -> !BinaryUtils.isEmpty(each.getUniqueCode())).collect(Collectors.toMap(ESDiagramLink::getUniqueCode, each -> each, (k1, k2) -> k2));
                releaseLinkKeyMap = releaseLinkList.stream().collect(Collectors.toMap(ESDiagramLink::getKey, each -> each, (k1, k2) -> k2));
            }
        }
        Map<String, String> keyNameMap = new HashMap<>(nodeList.size());
        if (!BinaryUtils.isEmpty(nodeList)) {
            for (ESDiagramNode node : nodeList) {
                //普通形状节点
                if (BinaryUtils.isEmpty(node.getCiCode())) {
                    ESDiagramNode releaseNode = releaseNodeKeyMap.get(node.getKey());
                    if (BinaryUtils.isEmpty(releaseNode)) {
                        result.setAdd(true);
                    } else if (!JSON.parse(releaseNode.getNodeJson()).equals(JSON.parse(node.getNodeJson()))) {
                        result.setUpdate(true);
                    }
                } else {
                    DiagramNodeJson json = JSON.parseObject(node.getNodeJson(), DiagramNodeJson.class);
                    keyNameMap.put(node.getKey(), json.getLabel());
                    EamDataUpdateInfo info = new EamDataUpdateInfo(node.getCiCode(), json.getClassName(), true, Lists.newArrayList(json.getLabel()));
                    ESDiagramNode releaseNode = releaseNodeMap.get(node.getCiCode());
                    if (releaseNode != null && releaseNode.getNodeJson() != null) {
                        Object releaseObj = JSON.parse(releaseNode.getNodeJson());
                        if (!releaseObj.equals(JSON.parse(node.getNodeJson()))) {
                            info.setUpdate(true);
                            result.setUpdate(true);
                            dataInfo.addAll(checkDataModelAttrInfo(json, releaseNode.getNodeJson()));
                        } else {
                            info.setAdd(false);
                            //判断是否有实体属性
                            if (CollectionUtils.isNotEmpty(json.getItems())) {
                                json.getItems().forEach(item -> dataInfo.add(new EamDataUpdateInfo(item.getCiCode(), "实体属性", true, Lists.newArrayList(item.getLabel() + "【" + json.getLabel() + "】"), false)));
                            }
                        }
                    } else {
                        //判断是否有实体属性
                        if (CollectionUtils.isNotEmpty(json.getItems())) {
                            json.getItems().forEach(item -> dataInfo.add(new EamDataUpdateInfo(item.getCiCode(), "实体属性", true, Lists.newArrayList(item.getLabel() + "【" + json.getLabel() + "】"))));
                        }
                        result.setAdd(true);
                    }
                    dataInfo.add(info);
                }
            }
        }
        if (!BinaryUtils.isEmpty(releaseNodeList)) {
            for (ESDiagramNode node : releaseNodeList) {
                if (BinaryUtils.isEmpty(node.getCiCode()) || BinaryUtils.isEmpty(node.getNodeJson())) {
                    continue;
                }
                if (!BinaryUtils.isEmpty(nodeMap.get(node.getCiCode()))) {
                    continue;
                }
                DiagramNodeJson json = JSON.parseObject(node.getNodeJson(), DiagramNodeJson.class);
                EamDataUpdateInfo info = new EamDataUpdateInfo(node.getCiCode(), json.getClassName(), true, Lists.newArrayList(json.getLabel()));
                info.setDelete(true);
                result.setDelete(true);
                keyNameMap.put(node.getKey(), json.getLabel());
                dataInfo.add(info);
            }
        }
        if (!BinaryUtils.isEmpty(linkList)) {
            for (ESDiagramLink link : linkList) {
                JSONObject obj = JSON.parseObject(link.getLinkJson());
                if (BinaryUtils.isEmpty(link.getUniqueCode())) {
                    ESDiagramLink releaseLink = releaseLinkKeyMap.get(link.getKey());
                    if (BinaryUtils.isEmpty(releaseLink)) {
                        result.setAdd(true);
                    } else {
                        String point = obj.getString("points");
                        String stroke = obj.getString("stroke");
                        String strokeWidth = obj.getString("strokeWidth");
                        if (BinaryUtils.isEmpty(point) || BinaryUtils.isEmpty(stroke) || BinaryUtils.isEmpty(strokeWidth)) {
                            continue;
                        }
                        JSONObject releaseObj = JSON.parseObject(releaseLink.getLinkJson());
                        String releasePoint = releaseObj.getString("points");
                        String releaseStroke = releaseObj.getString("stroke");
                        String releaseStrokeWidth = releaseObj.getString("strokeWidth");
                        if (!point.equals(releasePoint) || !stroke.equals(releaseStroke) || !strokeWidth.equals(releaseStrokeWidth)) {
                            result.setUpdate(true);
                        }
                    }
                } else {
                    String from = obj.getString("from");
                    String to = obj.getString("to");
                    if (BinaryUtils.isEmpty(from) || BinaryUtils.isEmpty(to)) {
                        continue;
                    }
                    String className = obj.getString("className");
                    String fromName = keyNameMap.get(from);
                    String toName = keyNameMap.get(to);
                    EamDataUpdateInfo info = new EamDataUpdateInfo(link.getUniqueCode(), className, false, Lists.newArrayList(fromName, toName));
                    if (!BinaryUtils.isEmpty(releaseLinkMap.get(link.getUniqueCode()))) {
                        String linkItems = obj.getString("linkItems");
                        String stroke = obj.getString("stroke");
                        String strokeWidth = obj.getString("strokeWidth");
                        String releaseJson = releaseLinkMap.get(link.getUniqueCode()).getLinkJson();
                        JSONObject releaseObj = JSON.parseObject(releaseJson);
                        String releaseLinkItems = releaseObj.getString("linkItems");
                        String releaseStroke = releaseObj.getString("stroke");
                        String releaseStrokeWidth = releaseObj.getString("strokeWidth");
                        if (BinaryUtils.isEmpty(linkItems) || BinaryUtils.isEmpty(stroke) || BinaryUtils.isEmpty(strokeWidth) ||
                                !linkItems.equals(releaseLinkItems) || !stroke.equals(releaseStroke) || !strokeWidth.equals(releaseStrokeWidth)) {
                            info.setUpdate(true);
                            result.setUpdate(true);
                        } else {
                            info.setAdd(false);
                        }
                    } else {
                        result.setAdd(true);
                    }
                    dataInfo.add(info);
                }
            }
        }
        if (!BinaryUtils.isEmpty(releaseLinkList)) {
            for (ESDiagramLink link : releaseLinkList) {
                if (BinaryUtils.isEmpty(link.getUniqueCode()) || BinaryUtils.isEmpty(link.getLinkJson())) {
                    continue;
                }
                if (!BinaryUtils.isEmpty(linkMap.get(link.getUniqueCode()))) {
                    continue;
                }
                JSONObject json = JSON.parseObject(link.getLinkJson());
                String from = json.getString("from");
                String to = json.getString("to");
                if (BinaryUtils.isEmpty(from) || BinaryUtils.isEmpty(to)) {
                    continue;
                }
                String className = json.getString("className");
                String fromName = keyNameMap.get(from);
                String toName = keyNameMap.get(to);
                EamDataUpdateInfo info = new EamDataUpdateInfo(link.getUniqueCode(), className, false, Lists.newArrayList(fromName, toName));
                info.setDelete(true);
                result.setDelete(true);
                dataInfo.add(info);
            }
        }
        List<EamDataUpdateInfo> removeList = new ArrayList<>();
        for (EamDataUpdateInfo each : dataInfo) {
            if (!each.getAdd() && !each.getUpdate() && !each.getDelete()) {
                removeList.add(each);
            }
        }
        dataInfo.removeAll(removeList);
        result.setDataInfo(dataInfo);
    }

    private List<EamDataUpdateInfo> checkDataModelAttrInfo(DiagramNodeJson json, String releaseJson) {
        List<EamDataUpdateInfo> dataInfo = new ArrayList<>();
        if (CollectionUtils.isEmpty(json.getItems())) {
            return dataInfo;
        }
        DiagramNodeJson releaseObj = JSON.parseObject(releaseJson, DiagramNodeJson.class);
        if (CollectionUtils.isEmpty(releaseObj.getItems())) {
            json.getItems().forEach(item -> dataInfo.add(new EamDataUpdateInfo(item.getCiCode(), "实体属性", true, Lists.newArrayList(item.getLabel() + "【" + json.getLabel() + "】"))));
            return dataInfo;
        }
        Map<String, String> releaseMap = releaseObj.getItems().stream().collect(Collectors.toMap(DataModelEntityNodeVo::getCiCode, each -> each.getAttrs().toString()));
        Map<String, String> releaseLabelMap = releaseObj.getItems().stream().collect(Collectors.toMap(DataModelEntityNodeVo::getCiCode, DataModelEntityNodeVo::getLabel));
        Map<String, EamDataUpdateInfo> dataMap = new HashMap<>();
        for (DataModelEntityNodeVo item : json.getItems()) {
            EamDataUpdateInfo info = new EamDataUpdateInfo(item.getCiCode(), "实体属性", true, Lists.newArrayList(item.getLabel() + "【" + json.getLabel() + "】"));
            String attr = releaseMap.get(item.getCiCode());
            if (attr != null) {
                if (item.getAttrs().toString().equals(attr)) {
                    info.setAdd(false);
                } else {
                    info.setUpdate(true);
                }
            }
            dataMap.put(item.getCiCode(), info);
            dataInfo.add(info);
        }
        for (Map.Entry<String, String> each : releaseMap.entrySet()) {
            EamDataUpdateInfo eachInfo = dataMap.get(each.getKey());
            if (eachInfo == null) {
                EamDataUpdateInfo info = new EamDataUpdateInfo(each.getKey(), "实体属性", true, Lists.newArrayList(releaseLabelMap.get(each.getKey()) + "【" + json.getLabel() + "】"));
                info.setDelete(true);
                dataInfo.add(info);
            }
        }
        return dataInfo;
    }

    @Override
    public List<ESDiagramDTO> queryDiagramByIds(BaseQueryDiagramDto dto) {
        List<ESDiagramDTO> diagramDTOS = diagramApiClient.queryFullDiagramByIds(dto.getDiagramIds());
        if (StringUtils.isBlank(dto.getProcessInstanceId())) {
            return diagramDTOS;
        }
        //查视图提交者信息
        SysUser submitter = null;
        PorcessResponse porcessResponse = flowableFeign.getProcessInstanceByProcessInstanceId(dto.getProcessInstanceId());
        if (porcessResponse != null && StringUtils.isNotBlank(porcessResponse.getProcessStartUserId())) {
            String processStartUserId = porcessResponse.getProcessStartUserId();
            CSysUser cdt = new CSysUser();
            cdt.setLoginCodeEqual(processStartUserId);
            List<SysUser> sysUsers = userApiSvc.getSysUserByCdt(cdt);
            if (CollectionUtils.isEmpty(sysUsers)) {
                submitter = new SysUser();
                submitter.setLoginCode(processStartUserId);
                submitter.setUserName(processStartUserId);
            } else {
                submitter = EamUtil.copy(sysUsers.get(0), SysUser.class);
            }
        }
        for (ESDiagramDTO diagramDTO : diagramDTOS) {
            if (submitter == null) {
                diagramDTO.setSubmitter(EamUtil.copy(diagramDTO.getCreator(), SysUser.class));
                continue;
            }
            diagramDTO.setSubmitter(submitter);
        }
        return diagramDTOS;
    }
}
